﻿/*
** Xin YUAN, 2022, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////
#ifndef __CTL_MACRO_H__
#define __CTL_MACRO_H__
////////////////////////////////////////////////////////////////////////////////

#ifndef __CTL_DEF_H__
	#error macro.h requires def.h to be included first.
#endif

////////////////////////////////////////////////////////////////////////////////
namespace CTL {
////////////////////////////////////////////////////////////////////////////////

// format : ${XXX}$, $XXX{XXX}$ or $XXX{}$

struct MacroItem
{
	uintptr_t uStart, uEnd;
	uintptr_t uActStart, uActEnd;
	uintptr_t uParamStart, uParamEnd;
};

inline bool FindMacro(const char* sz, uintptr_t uLen, MacroItem& item) noexcept
{
	int state = 1;
	const char* szp = sz;
	for ( uintptr_t i = 0; i < uLen; i ++ ) {
		char ch = *szp ++;
		if ( ch == '\0' )
			break;
		switch ( state ) {
		case 1:
			if ( ch == '$' ) {
				state = 2;
				item.uStart = szp - sz - 1;
				item.uActStart = item.uActEnd = szp - sz;
			}
			break;
		case 2:
			if ( ch == '_' || CharTraits<char>::IsAlpha(ch) ) {
				state = 3;
			}
			else if ( ch == '{' ) {
				state = 4;
				item.uParamStart = item.uParamEnd = szp - sz;
			}
			else {
				-- szp;
				state = 1;
			}
			break;
		case 3:
			if ( ch == '_' || ch == '-' || CharTraits<char>::IsAlnum(ch) ) {
			}
			else if ( ch == '{' ) {
				state = 4;
				item.uActEnd = szp - sz - 1;
				item.uParamStart = item.uParamEnd = szp - sz;
			}
			else {
				-- szp;
				state = 1;
			}
			break;
		case 4:
			if ( ch == '_' || CharTraits<char>::IsAlpha(ch) ) {
				state = 5;
			}
			else if ( ch >= '1' && ch <= '9' ) {
				state = 6;
			}
			else if ( ch == '}' ) {
				state = 7;
			}
			else {
				-- szp;
				state = 1;
			}
			break;
		case 5:
			if ( ch == '_' || ch == '-' || CharTraits<char>::IsAlnum(ch) ) {
			}
			else if ( ch == '}' ) {
				state = 7;
				item.uParamEnd = szp - sz - 1;
			}
			else {
				-- szp;
				state = 1;
			}
			break;
		case 6:
			if ( CharTraits<char>::IsDigit(ch) ) {
			}
			else if ( ch == '}' ) {
				state = 7;
				item.uParamEnd = szp - sz - 1;
			}
			else {
				-- szp;
				state = 1;
			}
			break;
		case 7:
			if ( ch == '$' ) {
				if ( item.uParamStart != item.uParamEnd
					|| item.uActStart != item.uActEnd ) {
					item.uEnd = szp - sz;
					return true;
				}
				//ACT & PARAM are empty
				-- szp;
			}
			state = 1;
			break;
		default:
			assert(false);
			break;
		}
	} //end for
	return false;
}

////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
